import os, time, logging, sys
from utils import *
from sql import *
from fastapi.responses import FileResponse
from traceback import print_exc

class ApiManager:
    def __init__(self) -> None:
        self.api: dict[Api] = {}
    
    def get(self, ver):
        return self.api[int(ver[1:])]
    
    def new(self, ver: int):
        self.api[ver] = eval(f"Api_v{ver}('./serveroot')")

class Api:
    def __init__(self, root_dir) -> None:
        self.init_logger()
        self.root_dir = root_dir
        self.mods_dir = os.path.join(root_dir, "mods")
        self.packages_dir = os.path.join(root_dir, "packages")
        self.package_index = File(os.path.join(self.root_dir, "packages", "index.json")).readJson()
        self.mods_index = File(os.path.join(self.root_dir, "mods", "index.json")).readJson()
        self.__timestamp = time.time_ns()
        self.update_timestamp()

    def init_logger(self):
        self.logger = logging.Logger(self.__class__.__name__)
        
        filename = f"logs/{time.strftime(r'%Y-%m-%d_%H-%M-%S')}.log"
        File(filename).create()

        file_handler = logging.FileHandler(filename)
        file_formatter = logging.Formatter('%(asctime)s | "%(name)s.%(funcName)s" <%(filename)s:%(lineno)d> [%(levelname)s] %(message)s')
        file_handler.setFormatter(file_formatter)
        file_handler.setLevel(logging.DEBUG)
        self.logger.addHandler(file_handler)
        
        console_handler = logging.StreamHandler()
        console_formatter = logging.Formatter('%(asctime)s | "%(name)s.%(funcName)s" <%(filename)s:%(lineno)d> [%(levelname)s] %(message)s')
        console_handler.setFormatter(console_formatter)
        console_handler.setLevel(logging.DEBUG)
        self.logger.addHandler(console_handler)
        self.logger.info("Api logging system started successfully!")
    
    def update_timestamp(self):
        self.__timestamp = time.time_ns()
        self.logger.info("Timestamp updated: " + str(self.__timestamp))

    def timestamp(self):
        return {"timestamp":self.__timestamp}

    def get_package(self, packname: str):
        if packname not in self.package_index["name"]: return jsonify(404, Message("package not found"))
        return jsonify(200, File(os.path.join(self.root_dir, "packages", packname, "pack-latest.json")).readJson())
    
    def upload_package(self, packjson: str):
        packjson = json.loads(packjson)
        _dir = os.path.join(self.root_dir, "packages", packjson["name"])
        if packjson["name"] in self.package_index["name"]: return jsonify(403, message("package already existed"))
        os.makedirs(_dir)
        File(os.path.join(_dir, "pack-latest.json")).create().writeStr(packjson)
        return jsonify(200, message("package created successfully"))
    
    def get_mod(self, name, modver, gamever, translate_name):
        r = []
        if name is None: return jsonify(404, message("mod name required"))
        if gamever is None: return jsonify(404, message("mod version required"))
        if modver is None:
            for i in os.listdir(os.path.join(self.root_dir, "mods", name)):
                if i.split('-')[2] == gamever:
                    r.append(i)
            return jsonify(200, r)
        else:
            return FileResponse(Mod(name, modver, gamever, translate_name).get_file(self.root_dir))
    
    def upload_mod(self, name, modver, gamever, translate_name, filedata):
        m = File(Mod(name, modver, gamever, translate_name).get_file(self.root_dir))
        if os.path.exists(m.path): return jsonify(403, message("package already existed"))
        m.create()
        m.write(filedata.file.read())
        return jsonify(200, message("mod uploaded successfully"))
    
    def find_mods(self, modname: str = None, modver: str = None, gamever: str = None):
        r = [] # maybe list
        if not(modname or gamever):return False
        if modname is None or len(self.mods_index["name"].keys()) > len(self.mods_index["gamever"].keys()):
            for gv, mods in self.mods_index["gamever"].items():
                if gv != gamever: continue
                for mod in mods:
                    if modname is None and modver is None:
                        r.append(mod)
                    elif modname is None and mod["mod_version"] == modver:
                        r.append(mod)
                    elif mod["name"] == modname and modver is None:
                        r.append(mod)
                    elif mod["name"] == modname and mod["mod_version"] == modver:
                        r.append(mod)
        else:
            for mn, mods in self.mods_index["name"].items():
                if mn != modname: continue
                for mod in mods:
                    if gamever is None and modver is None:
                        r.append(mod)
                    elif gamever is None and mod["mod_version"] == modver:
                        r.append(mod)
                    elif mod["game_version"] == gamever and modver is None:
                        r.append(mod)
                    elif mod["game_version"] == gamever and mod["mod_version"] == modver:
                        r.append(mod)
        return r
    
    def find_packages(self, packname: str, packver: str = None, gamever: str = None):
        r = [] # maybe list
        if not(packname or gamever):return r
        if packname is None or len(self.package_index["name"].keys()) > len(self.package_index["gamever"].keys()):
            for gv, packs in self.package_index["gamever"].items():
                if gv != gamever: continue
                for pack in packs:
                    if packname is None and packver is None:
                        r.append(pack)
                    elif packname is None and pack["pack_version"] == packver:
                        r.append(pack)
                    elif pack["name"] == packname and packver is None:
                        r.append(pack)
                    elif pack["name"] == packname and pack["mod_version"] == packver:
                        r.append(pack)
        else:
            for mn, packs in self.package_index["name"].items():
                if mn != packname: continue
                for pack in packs:
                    if gamever is None and packver is None:
                        r.append(pack)
                    elif gamever is None and pack["pack_version"] == packver:
                        r.append(pack)
                    elif pack["game_version"] == gamever and packver is None:
                        r.append(pack)
                    elif pack["game_version"] == gamever and pack["pack_version"] == packver:
                        r.append(pack)
        return r
    
    def classify_mod(self, mod: Mod):
        if mod.modname not in self.mods_index["name"].keys():
            self.mods_index["name"][mod.modname] = list()
        if mod.game_version not in self.mods_index["gamever"].keys():
            self.mods_index["gamever"][mod.game_version] = list()
        self.mods_index["name"][mod.modname].append(mod.toJson())
        self.mods_index["gamever"][mod.game_version].append(mod.toJson())
    
    def classify_package(self, package: Package):
        self.package_index["all"].append(package.toJson())
        if package.config.name not in self.package_index["name"].keys():
            self.package_index["name"][package.config.name] = list()
        if package.config.game_version not in self.package_index["gamever"].keys():
            self.package_index["gamever"][package.config.game_version] = list()
        self.package_index["name"][package.config.name].append(package.toJson())
        self.package_index["gamever"][package.config.game_version].append(package.toJson())

    def index_all_mods(self):
        self.mods_index = {
            "index_by": ["name", "gamever"],
            "name": {},
            "gamever": {}
        }
        for modname in os.listdir(self.mods_dir):
            p = os.path.join(self.mods_dir, modname)
            if not os.path.isdir(p):continue
            for filename in os.listdir(p):
                if not os.path.isfile(os.path.join(p, filename)):continue
                self.classify_mod(Mod.build_by_fullname(filename))
    
    def index_all_packages(self):
        self.package_index = {
            "index_by": ["all", "name", "gamever"],
            "name": {},
            "gamever": {},
            "all": []
        }
        for packagename in os.listdir(self.packages_dir):
            p = os.path.join(self.packages_dir, packagename)
            if not os.path.isdir(p):continue
            for filename in os.listdir(p):
                if not os.path.isfile(os.path.join(p, filename)):continue
                self.classify_package(Package(os.path.join(p, filename)))
    
    def index_all(self):
        try:
            self.index_all_mods()
            self.index_all_packages()
            self.logger.info("Indexed successfully!")
        except:
            print_exc()
            self.logger.warn("Indexed failed!")
    
    def load_all(self):
        try:
            self.package_index = File(os.path.join(self.root_dir, "packages", "index.json")).readJson()
            self.mods_index = File(os.path.join(self.root_dir, "mods", "index.json")).readJson()
            self.logger.info("Loaded successfully!")
        except:
            print_exc()
            self.logger.warn("Loaded failed!")
    
    def save_all(self):
        try:
            File(os.path.join(self.root_dir, "packages", "index.json")).writeJson(self.package_index)
            File(os.path.join(self.root_dir, "mods", "index.json")).writeJson(self.mods_index)
            self.logger.info("Saved successfully!")
        except:
            print_exc()
            self.logger.critical("Saved failed!")

            print(self.package_index)
            print(self.mods_index)
    
    def admin(self, token: str, data: str):
        if token != "123":
            return jsonify(400, message("token error"))

        try:
            data = json.loads(data)
            if "exec" in data.keys():
                self._ = None
                exec(data["exec"])
                self.logger.info(f'remote terminal exec: {data["exec"]}')
        except Exception as ept:
            return jsonify(300, {"exception": str(ept)})

        return jsonify(200, message(str(self._)))

class Api_v1(Api):
    def __init__(self, root_dir) -> None:
        super().__init__(root_dir)
        self.db = SqliteMain("test.db")
        
    def get_mods_by_name(self, name):
        if name not in self.mods_index["name"].keys():
            return jsonify(404, message("mod not found"))
        return jsonify(200, self.mods_index["name"][name])
    
    def get_mods_by_gamever(self, gamever):
        if gamever not in self.mods_index["gamever"].keys():
            return jsonify(404, message("cannot find any mod in this game version"))
        return jsonify(200, self.mods_index["gamever"][gamever])
    
    def get_packages_by_name(self, name):
        if name not in self.package_index["name"].keys():
            return jsonify(404, message("package not found"))
        return jsonify(200, self.package_index["name"][name])
    
    def get_packages_by_gamever(self, gamever):
        if gamever not in self.package_index["gamever"].keys():
            return jsonify(404, message("cannot find any package in this game version"))
        return jsonify(200, self.package_index["gamever"][gamever])
    
    def get_mods_by_name_and_gamever(self, name, gamever):
        r = []
        
        if name not in self.mods_index["name"].keys():
            return jsonify(404, message("mod not found"))
        if gamever not in self.mods_index["gamever"].keys():
            return jsonify(404, message("cannot find any mod in this game version"))
        
        if len(self.mods_index["name"].keys()) > len(self.mods_index["gamever"].keys()):
            for _, mod in self.mods_index["gamever"].items():
                if mod["name"] == name and gamever == _: r.append(mod)
        else:
            for _, mod in self.mods_index["name"].items():
                if mod["name"] == name and gamever == _: r.append(mod)
        return jsonify(200, r)
    
    def get_packages_by_name_and_gamever(self, name, gamever):
        r = []
        
        if name not in self.package_index["name"].keys():
            return jsonify(404, message("mod not found"))
        if gamever not in self.package_index["gamever"].keys():
            return jsonify(404, message("cannot find any mod in this game version"))
        
        if len(self.package_index["name"].keys()) > len(self.package_index["gamever"].keys()):
            for _, pack in self.package_index["gamever"].items():
                if pack["name"] == name and gamever == _: r.append(pack)
        else:
            for _, pack in self.package_index["name"].items():
                if pack["name"] == name and gamever == _: r.append(pack)
        return jsonify(200, r)
    
    def get_mods(self, name=None, modver=None, gamever=None, translate_name=None):
        return jsonify(200, self.find_mods(name, modver, gamever))
    
    def get_packages(self, name=None, packver=None, gamever=None, translate_name=None):
        return jsonify(200, self.find_packages(name, packver, gamever))
    
    def get_hash(self, packid:int, file: str):
        return jsonify(200, self.db.select_col_where(packid, 'md5', [('name', SqliteOperator.EQUALS, file)])[0])
